package examples.ld;

import java.util.Iterator;
import java.util.LinkedList;
import java.util.List;
import java.util.TreeSet;

import org.jgap.BaseGeneticOperator;
import org.jgap.Configuration;
import org.jgap.Gene;
import org.jgap.IChromosome;
import org.jgap.InvalidConfigurationException;
import org.jgap.Population;
import org.jgap.RandomGenerator;
import org.jgap.impl.GreedyCrossover;
import org.jgap.impl.IntegerGene;
import org.jgap.util.ICloneable;

public class ContainerOrderCrossover extends BaseGeneticOperator {

    private Container[] contChrom;
    private double crossRate;
    private boolean adaptive;
    boolean ASSERTIONS = true;
    private int m_startOffset = 0;

    // public ContainerGreedyCrossOperator() throws
    // InvalidConfigurationException {
    // super (Genotype.getStaticConfiguration());
    // }
    public ContainerOrderCrossover(Configuration a_configuration)
            throws InvalidConfigurationException {
        super(a_configuration);

    }

    public ContainerOrderCrossover(Configuration a_configuration,
            Container[] container, double crossoverRate, boolean adapt)
            throws InvalidConfigurationException {
        super(a_configuration);
        contChrom = container;
        adaptive = adapt;
        crossRate = crossoverRate;
        // setCrossoverRate()

    }

    public double distance(final Object a_from, final Object a_to) {
        IntegerGene from = (IntegerGene) a_from;
        IntegerGene to = (IntegerGene) a_to;
        return Math.abs(to.intValue() - from.intValue());
    }

    public void operate(final Population a_population,
            final List a_candidateChromosomes) {
        int size = Math.min(getConfiguration().getPopulationSize(),
                a_population.size());
        // if 0.6, then 0.3*size times cross over,each take two
        int numCrossovers = (int) (size * crossRate);
        RandomGenerator generator = getConfiguration().getRandomGenerator();

        // For each crossover, grab two random chromosomes and do what
        // Grefenstette et al say.
        // --------------------------------------------------------------
        for (int i = 0; i < numCrossovers; i++) {
            IChromosome firstMate = (IChromosome) ((ICloneable) a_population.getChromosome(
                    generator.nextInt(size))).clone();
            IChromosome secondMate = (IChromosome) ((ICloneable) a_population.getChromosome(
                    generator.nextInt(size))).clone();
            operate(firstMate, secondMate);
            // Add the modified chromosomes to the candidate pool so that
            // they'll be considered for natural selection during the next
            // phase of evolution.
            // -----------------------------------------------------------
            a_candidateChromosomes.add(firstMate);
            a_candidateChromosomes.add(secondMate);
        }
    }

    public void operate(final IChromosome a_firstMate,
            final IChromosome a_secondMate) {
        Gene[] g1 = a_firstMate.getGenes();
        Gene[] g2 = a_secondMate.getGenes();
        Gene[] c1, c2;
        try {
            RandomGenerator generator = getConfiguration().getRandomGenerator();
            int pos = generator.nextInt(128 - 16);// 128
            if (pos % 2 == 1) {

                // if no 40ft was cut,continue.
                boolean typeCheck = false;

                int id1 = (Integer) g1[pos].getAllele();
                int id2 = (Integer) g1[pos + 16].getAllele();
                if ((contChrom[id1].getType() != 20)
                        || (contChrom[id2].getType() != 20)) {
                    typeCheck = false;
                }
                int id3 = (Integer) g2[pos].getAllele();
                int id4 = (Integer) g2[pos + 16].getAllele();
                if ((contChrom[id3].getType() != 20)
                        || (contChrom[id4].getType() != 20)) {
                    typeCheck = false;
                }
                if (typeCheck) {
                    pos = pos;
                } else {
                    pos -= 1;
                }

                // pos -=1;
            } else {
                // split point involve no 40 ft since start wiht even number
                // position
                pos = pos;
            }
            c1 = operate(g1, g2, pos);
            c2 = operate(g1, g2, pos);
            // c2 = operate(g2, g1, pos);
            // c1 = g2;
            // c2 = g2;
            a_firstMate.setGenes(c1);
            a_secondMate.setGenes(c2);
        } catch (InvalidConfigurationException cex) {
            throw new Error("Error occured while operating on:" + a_firstMate
                    + " and " + a_secondMate + ". First " + m_startOffset
                    + " genes were excluded "
                    + "from crossover. Error message: " + cex.getMessage());
        }
    }

    protected Gene[] operate(final Gene[] a_g1, final Gene[] a_g2, int position) {
        int n = a_g1.length;
        LinkedList out = new LinkedList();
        TreeSet not_picked = new TreeSet();
        // out.add(a_g1[m_startOffset]);

        Gene[] g = new Gene[n];

        for (int j = position; j < position + 16; j++) { // g[m_startOffset]
            // picked
            int test;
            test = (Integer) a_g2[j].getAllele();
            not_picked.add(a_g2[j].getAllele());

        }

        for (int j = 0; j < n; j++) { // g[m_startOffset] picked
            // if (ASSERTIONS && not_picked.contains(a_g1[j])) {
            // throw new Error("All genes must be different for "
            // + getClass().getName() + ". The gene "
            // + a_g1[j] + "[" + j + "] occurs more "
            // + "than once in one of the chromosomes. ");
            // }
            // if((j<position)||(j>position+15))
            if (!not_picked.contains(a_g1[j].getAllele())) {
                int test;
                test = (Integer) a_g1[j].getAllele();
                Gene t = a_g1[j];
                out.add(t);
            } else {

                int test = 0;
                test = 0;
            }
        }
        /*
         * for (int j = 0; j < 128; j++) { IntegerGene contIndex = (IntegerGene)
         * a_g1[j]; Container cont = (Container)
         * contChrom[contIndex.intValue()]; double weight = cont.getWeight();
         * int type = cont.getType(); int cid = cont.getId(); int port =
         * cont.getPort();
         * 
         * System.out.println("  Cont at index " + j + " with weight " + weight
         * + " type: " + type + " id: " + cid + " port: " + port ); }
         */
        Iterator gi = out.iterator();
        for (int i = 0; i < n; i++) { // g[m_startOffset] picked

            if ((i < position) || (i > position + 15)) {
                // int test = (Integer)gi.next();
                // g[i].setAllele( new Integer(test)) ;
                g[i] = (Gene) gi.next();

                int test;
                test = out.size();
                test = (Integer) g[i].getAllele();
                test = (Integer) a_g1[i].getAllele();
            } else {
                // g[i].setAllele((Integer)a_g2[i].getAllele()) ;
                g[i] = a_g2[i];
                int test;
                test = (Integer) g[i].getAllele();
                test = (Integer) a_g1[i].getAllele();
            }
        }
        /*
         * for (int j = 0; j < 128; j++) { IntegerGene contIndex = (IntegerGene)
         * g[j]; Container cont = (Container) contChrom[contIndex.intValue()];
         * double weight = cont.getWeight(); int type = cont.getType(); int cid
         * = cont.getId(); int port = cont.getPort();
         * 
         * System.out.println("  Cont at index " + j + " with weight " + weight
         * + " type: " + type + " id: " + cid + " port: " + port ); }
         */

        // check and repair
        for (int i = 0; i < 128 - 2; i++) {

            int id = (Integer) g[i].getAllele();
            i++;// even number only
            int id1 = (Integer) g[i].getAllele();
            int id2 = (Integer) g[i + 1].getAllele();
            if ((contChrom[id].getType() == 40)
                    && (contChrom[id1].getType() == 40)) {
                if (contChrom[id].getId() != contChrom[id1].getId()) {
                    Gene temp = g[i - 1];
                    g[i - 1] = g[i];
                    g[i] = g[i + 1];
                    g[i + 1] = temp;

                }
            }
            if (contChrom[id].getType() == 20) {
                if (contChrom[id1].getType() == 40) {
                    Gene temp = g[i - 1];
                    g[i - 1] = g[i];
                    // g[i] = g[i+1];
                    // g[i+1] = temp;
                    for (int j = i + 1; j < 128; j++) {
                        Gene pair = g[j];
                        id = (Integer) g[j].getAllele();
                        if (contChrom[id].getId() == contChrom[id1].getId()) {
                            g[i] = pair;
                            g[j] = temp;
                        }
                    }

                }
            }

        }
        /*
         * for (int j = 0; j < 128; j++) { IntegerGene contIndex = (IntegerGene)
         * g[j]; Container cont = (Container) contChrom[contIndex.intValue()];
         * double weight = cont.getWeight(); int type = cont.getType(); int cid
         * = cont.getId(); int port = cont.getPort();
         * 
         * System.out.println("  Cont at index " + j + " with weight " + weight
         * + " type: " + type + " id: " + cid + " port: " + port ); }
         */

        for (int i = 0; i < 128 - 2; i++) {

            int id = (Integer) g[i].getAllele();
            i++;// even number only
            int id1 = (Integer) g[i].getAllele();
            // int id2 = (Integer)g[i+1].getAllele();
            boolean wrong = false;
            if (contChrom[id].getType() == 20) {
                if (contChrom[id1].getType() == 40) {
                    wrong = true;
                }
            }
            if (contChrom[id].getType() == 40) {
                if (contChrom[id1].getType() == 20) {
                    wrong = true;
                }
            }
            if ((contChrom[id].getType() == 40)
                    && (contChrom[id1].getType() == 40)) {
                if (contChrom[id].getId() != contChrom[id1].getId()) {
                    wrong = true;
                }
            }
            if (wrong) {
                // Gene temp = g[i-1];
                // g[i-1] = g[i];
                // g[i] = g[i+1];
                // g[i+1] = temp;
                // id = id1;
                // id1 = id2;
                for (int j = 0; j < 128; j++) {
                    IntegerGene contIndex = (IntegerGene) g[j];
                    Container cont = (Container) contChrom[contIndex.intValue()];
                    double weight = cont.getWeight();
                    int type = cont.getType();
                    int cid = cont.getId();
                    int port = cont.getPort();

                    System.out.println("  Cont at index " + j + " with weight "
                            + weight + " type: " + type + " id: " + cid
                            + " port: " + port);
                }
                // break;

            }
        }

        return g;
    }

    protected Gene findNext(final Gene[] a_g, final Gene a_x) {
        for (int i = m_startOffset; i < a_g.length - 1; i++) {
            if (a_g[i].equals(a_x)) {
                return a_g[i + 1];
            }
        }
        return null;
    }

    public void setStartOffset(int a_offset) {
        m_startOffset = a_offset;
    }

    public int getStartOffset() {
        return m_startOffset;
    }

    public int compareTo(final Object a_other) {
        if (a_other == null) {
            return 1;
        }
        GreedyCrossover op = (GreedyCrossover) a_other;
        if (getStartOffset() < op.getStartOffset()) {
            // start offset less, meaning more to do --> return 1 for
            // "is greater than"
            return 1;
        } else if (getStartOffset() > op.getStartOffset()) {
            return -1;
        } else {
            // Everything is equal. Return zero.
            // ---------------------------------
            return 0;
        }
    }

    public double getRate() {
        return crossRate;
    }

    public void setRate(double rate) {
        crossRate = rate;
    }

    public void setAdaptiveRate(boolean state) {
        if ((state) && (adaptive)) {
            if (crossRate < 0.942)//0.99max
            {
                crossRate *= 1.05;
            }
        }
        //	crossRate = rate;
    }
}
